/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package abricots.entity.state;

import abricots.MyLog;
import abricots.entity.Entity;
import com.esotericsoftware.minlog.Log;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

/**
 *
 * @author charly
 */
public class BasicEntityStateManager implements EntityStateManager {

    protected MyLog log = new MyLog(getClass().getSimpleName(), Log.LEVEL_DEBUG);
    protected final List<EntityState> states;
    protected final StateComparator comparator;
    protected final Entity entity;
    protected long lastValid = -1;
    private boolean sortNeeded;

    public BasicEntityStateManager(Entity entity) {
        states = Collections.synchronizedList(new LinkedList<EntityState>());
        comparator = new StateComparator();
        this.entity = entity;
    }

    @Override
    public void setLastValid(long lastValid) {
        log.trace("Last valid state set to " + lastValid);
        this.lastValid = lastValid;
    }

    @Override
    public long getLastValid() {
        return lastValid;
    }

    @Override
    public void addState(EntityState state) {
        if (state == null) {
            log.warn("  State is null");
            return;
        }
        BasicEntityState basicEntityState = (BasicEntityState) state;
        basicEntityState.setEntityId(entity.getId());
        log.trace("New state to be added: " + basicEntityState.toString());
        synchronized (states) {
            if (states.contains(state)) {
                log.trace("  State already present");
                return;
            }
            Iterator<EntityState> it = states.iterator();
            while (it.hasNext()) {
                EntityState knownState = it.next();
                if (knownState.getGameTime() == state.getGameTime()) {
                    log.debug("Overwriting state: old: " + knownState.toString());
                    log.debug("              with new: " + state.toString());
                    it.remove();
                    break;
                }
            }
            states.add(state);
            sortNeeded = true;
//            Collections.sort(states, comparator);
        }
    }

    @Override
    public EntityState getEntityState(long gameTime) {
        sortStates();
        synchronized (states) {
            for (EntityState state : states) {
                if (state.getGameTime() == gameTime) {
                    return state;
                }
                if (state.getGameTime() > gameTime) {
                    return null;
                }
            }
        }
        return null;
    }

    @Override
    public EntityState getLastEntityState() {
        synchronized (states) {
            if (states.isEmpty()) {
                return null;
            }
            return states.get(states.size() - 1);
        }
    }

    @Override
    public EntityState getLastEntityStateUpTo(long gameTime) {
        EntityState lastState = null;
        sortStates();
        synchronized (states) {
            if (gameTime == -1 && !states.isEmpty()) {
                return states.get(states.size() - 1);
            }
            for (EntityState state : states) {
                if (state.getGameTime() > gameTime) {
                    break;
                }
                lastState = state;
            }
            if (lastState == null && !states.isEmpty()) {
                lastState = states.get(0);
                if (gameTime != 0) {
                    log.debug("No entity state prior to " + gameTime + ", returning oldest one at " + lastState.getGameTime());
                }
            }
            return lastState;
        }
    }

    @Override
    public void clearAllBefore(long gameTime) {
        sortStates();
        synchronized (states) {
            Iterator<EntityState> it = states.iterator();
            while (it.hasNext()) {
                EntityState next = it.next();
                if (next.getGameTime() < gameTime) {
                    it.remove();
                } else {
                    break;
                }
            }
            log.trace("Discarded states before " + gameTime + ", " + states.size() + " left");
        }
    }

    @Override
    public void clearAllAfter(long gameTime) {
        sortStates();
        synchronized (states) {
            Iterator<EntityState> it = states.iterator();
            while (it.hasNext()) {
                EntityState next = it.next();
                if (next.getGameTime() <= gameTime) {
                    continue;
                }
                it.remove();


            }
            log.trace("Discarded states after " + gameTime + ", " + states.size() + " left");
        }
    }

    private void sortStates() {
        if (!sortNeeded) {
            return;
        }
        synchronized (states) {
            Collections.sort(states, comparator);
            sortNeeded = false;
        }
    }

    protected class StateComparator implements Comparator<EntityState> {

        @Override
        public int compare(EntityState o1, EntityState o2) {
            return (int) (o1.getGameTime() - o2.getGameTime());
        }
    }
}
